package cn.bearspark.gateway.filter;

import cn.bearspark.common.enumeration.JwtClaimsKeyEnum;
import cn.bearspark.common.enumeration.RequestHeaderNameEnum;
import cn.bearspark.common.util.JwtUtil;
import cn.bearspark.gateway.config.UrlListProperties;
import cn.bearspark.gateway.exception.NoTokenException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Map;

/**
 * 校验 token 的过滤器，优先级最高
 *
 * @author f
 */
@Component
public class TokenCheckFilter implements GlobalFilter, Ordered {

    private static final Logger LOGGER = LoggerFactory.getLogger(TokenCheckFilter.class);

    private final UrlListProperties urlListProperties;

    public TokenCheckFilter(UrlListProperties urlListProperties) {
        this.urlListProperties = urlListProperties;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String requestUrl = request.getURI().getPath();

        // 如果 url 在白名单中，则直接通过
        for (String whiteUrl : urlListProperties.getWhiteUrlList()) {
            if (requestUrl.startsWith(whiteUrl)) {
                return chain.filter(exchange);
            }
        }

        // 如果 url 在灰名单中，则无需向前端报没有 token 的异常
        boolean isGrayUrl = false;
        for (String grayUrl : urlListProperties.getGrayUrlList()) {
            if (requestUrl.startsWith(grayUrl)) {
                isGrayUrl = true;
                break;
            }
        }

        // 校验 token 是否存在且不为空，如果不存在或为空，则不通过
        List<String> tokenList = request.getHeaders().get(RequestHeaderNameEnum.TOKEN.getName());
        if (CollectionUtils.isEmpty(tokenList)) {
            return tokenExceptionAction(exchange, chain, isGrayUrl);
        }
        String token = tokenList.get(0);
        if (!StringUtils.hasLength(token)) {
            return tokenExceptionAction(exchange, chain, isGrayUrl);
        }

        // 校验 token，如果失效，则不通过
        Map<String, Object> claims = JwtUtil.parseJWT(token);
        if (claims == null) {
            return tokenExceptionAction(exchange, chain, isGrayUrl);
        }
        String userIdStr = claims.get(JwtClaimsKeyEnum.USER_ID.getKey()).toString();
        Long userId = StringUtils.hasLength(userIdStr) ? Long.valueOf(userIdStr) : null;
        if (userId == null) {
            return tokenExceptionAction(exchange, chain, isGrayUrl);
        }

        // 给请求头中添加一个参数，便于之后的处理
        ServerHttpRequest.Builder builder = request.mutate();
        builder.header(RequestHeaderNameEnum.USER_ID.getName(), userIdStr);

        // 将参数传到下游
        return chain.filter(exchange.mutate().request(builder.build()).build());
    }

    // 在 token 发生问题的时候触发的操作：如果在灰名单中，则无需返回没有 token 的异常；如果不再，则需要返回
    private Mono<Void> tokenExceptionAction(ServerWebExchange exchange, GatewayFilterChain chain, boolean isGrayUrl) {
        return isGrayUrl ?
                chain.filter(exchange) :
                Mono.error(new NoTokenException());
    }

    // 将本过滤器的优先级设置为最高
    @Override
    public int getOrder() {
        return 0;
    }

}
